﻿using Microsoft.Extensions.Logging;
using Orleans;
using Orleans.Runtime;
using Orleans.Storage;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Gasgoo.ShareLibrary.Framework.iDataProvider
{
    public class GasgooDataStorageHandel : IGrainStorage, ILifecycleParticipant<ISiloLifecycle>
    {
        private readonly string ProviderName;
        private readonly IServiceProvider serviceProvider;
        private readonly ILogger<GasgooDataStorageHandel> logger;
        private readonly IDataStorageProvider storageProvider;

        public GasgooDataStorageHandel(string providerName, IServiceProvider serviceProvider, ILogger<GasgooDataStorageHandel> logger)
        {
            ProviderName = providerName;
            this.serviceProvider = serviceProvider;
            this.logger = logger;
            this.storageProvider = this.serviceProvider.GetRequiredServiceByName<IDataStorageProvider>(providerName);
        }

        #region IGrainStorage (执行优先级 2)
        public async Task ClearStateAsync(string grainType, GrainReference grainReference, IGrainState grainState)
        {
            try
            {
                var key = GetKey(grainType, grainReference, out string keyExtension);
                await storageProvider.RemoveAsync(grainType, key, keyExtension, grainState.State);
            }
            catch (Exception ex)
            {
                logger.LogError(ex.Message);
                throw;
            }
        }

        public async Task ReadStateAsync(string grainType, GrainReference grainReference, IGrainState grainState)
        {
            try
            {
                var key = GetKey(grainType, grainReference, out string keyExtension);
                var type = grainState.State.GetType();
                grainState.State = await storageProvider.GetAsync(grainType, key, keyExtension, type) ?? Activator.CreateInstance(type);

                if (grainState.State is AbstractDataEntity state)
                {
                    state.SetProviderName(this.ProviderName);
                }

            }
            catch (Exception ex)
            {
                logger.LogError(ex.Message);
                throw;
            }
        }

        public async Task WriteStateAsync(string grainType, GrainReference grainReference, IGrainState grainState)
        {
            try
            {
                var key = GetKey(grainType, grainReference, out string keyExtension);
                await storageProvider.AddOrUpdateAsync(grainType, key, keyExtension, grainState.State, grainState.State.GetType());
            }
            catch (Exception ex)
            {
                logger.LogError(ex.Message);
                throw;
            }
        }
        #endregion

        #region ILifecycleParticipant<ISiloLifecycle> (执行优先级 1)
        public void Participate(ISiloLifecycle lifecycle)
        {
            lifecycle.Subscribe(OptionFormattingUtilities.Name<GasgooDataStorageHandel>(ProviderName), ServiceLifecycleStage.ApplicationServices, Init, Close);
        }


        public Task Init(CancellationToken ct)
        {
            return Task.CompletedTask;
        }

        public Task Close(CancellationToken ct)
        {
            return Task.CompletedTask;
        }

        #endregion

        private string GetKey(string className, GrainReference grainReference, out string keyExtension)
        {
            long longKey = grainReference.GetPrimaryKeyLong(out keyExtension);
            if (longKey > 0)
            {
                return longKey.ToString();
            }

            string stringKey = grainReference.GetPrimaryKeyString();
            if (!string.IsNullOrWhiteSpace(stringKey))
            {
                return stringKey;
            }

            Guid guidKey = grainReference.GetPrimaryKey(out keyExtension);
            if (guidKey != default)
            {
                return guidKey.ToString();
            }

            return string.Empty;

            //var keyType = GasgooUtils.GetKeyTypeOfClass(className);
            //string key = string.Empty;

            //switch (keyType)
            //{
            //    case "long":
            //        key = grainReference.GetPrimaryKeyLong().ToString();
            //        break;
            //    case "string":
            //        key = grainReference.GetPrimaryKeyString();
            //        break;
            //    case "long-string":
            //        long longKey = grainReference.GetPrimaryKeyLong(out key);
            //        key = $"{longKey}_{key}";
            //        break;

            //    case "guid-string":
            //    case "guid":
            //    default:
            //        key = grainReference.ToKeyString();
            //        break;
            //}

            //return $"{className}_{key}";
        }

    }
}
